Service.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. "use client";
  2. import { getWheelApi } from "@/api/cashWheel";
  3. import { ServiceTypes } from "@/api/customservice";
  4. import { userInfoApi } from "@/api/login";
  5. import { lredPacketApi, redPacketApi } from "@/api/promo";
  6. import { getGiveInfoApi } from "@/api/slots";
  7. import UserRecharge, { ModalRefProps, Timeout } from "@/components/ModalPopup/RechargeModal";
  8. import RedPacketModal, { RedPacketModalProps } from "@/components/ModalPopup/RedPacketModal";
  9. import SignInModal, { SignInModalProps } from "@/components/ModalPopup/SignInModal";
  10. import SlotsModal, { SlotModalRefProps } from "@/components/ModalPopup/SlotsModal";
  11. import WheelModal, { WheelModalProps } from "@/components/ModalPopup/WheelModal";
  12. import { useEventPoint } from "@/hooks/useEventPoint";
  13. import { Link } from "@/i18n/routing";
  14. import { useModalShow } from "@/stores/modalShow";
  15. import { useFirstPayStore } from "@/stores/useFirstPayStore";
  16. import { useGlobalNoticeStore } from "@/stores/useGlobalNoticeStore";
  17. import { useSignStore } from "@/stores/useSignStore";
  18. import useWheelStore from "@/stores/useWheelStore";
  19. import { getToken } from "@/utils/Cookies";
  20. import { useRequest } from "ahooks";
  21. import { Badge } from "antd-mobile";
  22. import dayjs from "dayjs";
  23. import { useTranslations } from "next-intl";
  24. import Image from "next/image";
  25. import { usePathname } from "next/navigation";
  26. import { FC, useEffect, useMemo, useRef, useState } from "react";
  27. interface Props {
  28. services: ServiceTypes[];
  29. }
  30. interface SlotSectionProps {
  31. onDestory?: () => void;
  32. }
  33. /**
  34. * 免费送活动
  35. */
  36. const SlotSection: FC<SlotSectionProps> = ({ onDestory }) => {
  37. const slotsRef = useRef<SlotModalRefProps | null>(null);
  38. const getSlots = async () => {
  39. const token = getToken();
  40. if (token) {
  41. const result = await getGiveInfoApi();
  42. return result.data;
  43. } else {
  44. destory();
  45. return Promise.resolve({});
  46. }
  47. };
  48. const { data: slots, run: slotRun } = useRequest<any, any>(getSlots, {
  49. pollingInterval: 2000,
  50. pollingErrorRetryCount: 1,
  51. pollingWhenHidden: false,
  52. });
  53. const slotHandler = () => {
  54. slotsRef.current?.onOpen(slots);
  55. };
  56. const destory = () => {
  57. if (onDestory && typeof onDestory === "function") onDestory();
  58. };
  59. return (
  60. <>
  61. {slots?.id ? (
  62. <img
  63. src={"/slots/slots-icon.gif"}
  64. className={"mb-[0.2778rem] w-[100%]"}
  65. onClick={slotHandler}
  66. />
  67. ) : null}
  68. {/*随机送*/}
  69. <SlotsModal ref={slotsRef} onAfterHandler={slotRun} onClose={destory} />
  70. </>
  71. );
  72. };
  73. /**
  74. * 轮盘
  75. */
  76. const WheelSection = () => {
  77. const pathname = usePathname();
  78. const wheelModalRef = useRef<WheelModalProps | null>(null);
  79. const [data, setData] = useState<any>([]);
  80. const { modalShow, closeModalShow } = useModalShow((state: any) => ({
  81. modalShow: state.modalShow,
  82. closeModalShow: state.closeModalShow,
  83. }));
  84. const keyName = useMemo(() => {
  85. return "WheelSection";
  86. }, []);
  87. const { wheelStatus, wheelCurrent, setWheel } = useWheelStore((state) => ({
  88. wheelStatus: state.status,
  89. wheelCurrent: state.currentWheel,
  90. setWheel: state.setWheel,
  91. }));
  92. useEffect(() => {
  93. setWheel().then((data) => {
  94. if (data && useWheelStore.getState().status === 1) {
  95. setData(data);
  96. }
  97. });
  98. }, []);
  99. useEffect(() => {
  100. if (!data) return;
  101. if (modalShow === keyName) {
  102. wheelModalRef.current?.onOpen(data);
  103. }
  104. }, [modalShow, data, keyName, pathname]);
  105. const destoryComponent = () => {
  106. if (modalShow === keyName) {
  107. closeModalShow(keyName);
  108. }
  109. };
  110. return (
  111. <>
  112. {wheelStatus === 2 ? (
  113. <Link
  114. href={"/cashWheel"}
  115. className={
  116. "mb-[0.2778rem] flex h-[0.54rem] w-[0.54rem] items-center" +
  117. " justify-center rounded-[50%] bg-gradient-to-b from-[#0575e6] to-[#00f260]"
  118. }
  119. >
  120. <Image src={"/wheels/wheel-icon.gif"} alt={"wheel"} width={100} height={100} />
  121. </Link>
  122. ) : null}
  123. {/* 轮盘弹窗 */}
  124. <WheelModal ref={wheelModalRef} onDestory={destoryComponent} />
  125. </>
  126. );
  127. };
  128. /**
  129. * 首充
  130. */
  131. const PaySection = () => {
  132. const token = getToken();
  133. const { firstPay, getPayData } = useFirstPayStore((state) => {
  134. return {
  135. firstPay: state.first_pay,
  136. getPayData: state.getPayData,
  137. };
  138. });
  139. const userRechargeRef = useRef<ModalRefProps>(null);
  140. // 首充活动
  141. // const getPayInfo = async (): Promise<PayDataType> => {
  142. // if (token) {
  143. // const result = await getPaysApi();
  144. // return result.data;
  145. // } else {
  146. // return Promise.resolve({
  147. // first_pay: [],
  148. // pay: [],
  149. // });
  150. // }
  151. // };
  152. // const getPayInfo = async (): Promise<PayDataType> => {};
  153. const { data, run: payRun } = useRequest(getPayData, {
  154. pollingErrorRetryCount: 1,
  155. pollingWhenHidden: false,
  156. });
  157. return (
  158. <>
  159. {firstPay.map((item, index) => {
  160. return (
  161. <div
  162. key={index}
  163. className={`mb-[0.2778rem] flex cursor-pointer flex-col items-center`}
  164. >
  165. <img
  166. className={"w-[0.54rem]"}
  167. src="/hby/recharge.png"
  168. onClick={() => {
  169. userRechargeRef.current?.onOpen &&
  170. userRechargeRef.current?.onOpen(firstPay, index);
  171. }}
  172. />
  173. {item.count_down > 0 ? (
  174. <Timeout
  175. className={
  176. "relative before:left-0 before:top-0 before:content-['']" +
  177. " -m-[0.0417rem] before:h-[100%] before:blur-[0.0486rem]" +
  178. " before:absolute before:-z-10 before:w-[100%] before:bg-[#ed9d00]" +
  179. " text-[0.08rem]" +
  180. " z-1 text-[#fff]"
  181. }
  182. endTime={item.count_down}
  183. onEndHandler={payRun}
  184. />
  185. ) : null}
  186. </div>
  187. );
  188. })}
  189. {/*首充弹窗*/}
  190. <UserRecharge ref={userRechargeRef} />
  191. </>
  192. );
  193. };
  194. /**
  195. * 红包雨
  196. */
  197. const RedPacketSection = () => {
  198. const token = getToken();
  199. const redPacketModalRef = useRef<RedPacketModalProps>(null);
  200. const getRedPacketInfo = async () => {
  201. try {
  202. let redPacketInfo: any;
  203. let actList: any = [];
  204. if (token) {
  205. redPacketInfo = await lredPacketApi();
  206. actList = redPacketInfo.data?.red_packets || [];
  207. } else {
  208. redPacketInfo = await redPacketApi();
  209. actList = redPacketInfo.data || [];
  210. }
  211. // 已经开始
  212. return actList.filter((aItem: any) => {
  213. return aItem.is_start;
  214. });
  215. } catch (error) {}
  216. };
  217. // 红包雨轮询
  218. const { data: packets, run } = useRequest<any[], any>(getRedPacketInfo, {
  219. pollingInterval: 5000,
  220. pollingErrorRetryCount: 1,
  221. pollingWhenHidden: false,
  222. });
  223. return (
  224. <>
  225. {packets?.map((item, index) => {
  226. const icons = JSON.parse(item.icon);
  227. const icon = icons[icons.length - 1];
  228. return (
  229. <div key={index} className={`mb-[0.2778rem] cursor-pointer`}>
  230. <img
  231. className={"w-[0.54rem] object-fill"}
  232. src={icon}
  233. onClick={() => {
  234. redPacketModalRef.current?.onOpen(packets, index);
  235. }}
  236. />
  237. </div>
  238. );
  239. })}
  240. {/*红包雨弹窗*/}
  241. <RedPacketModal ref={redPacketModalRef} onAfterHandler={run} />
  242. </>
  243. );
  244. };
  245. const MessageSection = () => {
  246. const { unread, userUnred } = useGlobalNoticeStore((state) => ({
  247. unread: state.unread,
  248. userUnred: state.userUnred,
  249. }));
  250. return (
  251. <>
  252. {unread || userUnred ? (
  253. <Link
  254. href={"/notification"}
  255. className={
  256. "mb-[0.2778rem] flex h-[0.54rem] w-[0.54rem] items-center" +
  257. " justify-center rounded-[50%] bg-gradient-to-t from-[#ffa111]" +
  258. " to-[#ffcf35]"
  259. }
  260. >
  261. <Badge content={userUnred + unread} style={{ "--top": "12px" }}>
  262. <i className={"iconfont icon-duanxinguanli text-[0.3rem] text-[#fff]"}></i>
  263. </Badge>
  264. </Link>
  265. ) : null}
  266. </>
  267. );
  268. };
  269. /**
  270. * 客服
  271. */
  272. const CustomerSection: FC<Omit<Props, "socials">> = (props) => {
  273. const { services } = props;
  274. const defaultService = services?.filter((item) => item.is_suspend === 1);
  275. return (
  276. <>
  277. {defaultService?.map((item, index) => (
  278. <Link
  279. key={index}
  280. href={item.url}
  281. className={
  282. "flex h-[0.54rem] w-[0.54rem] items-center justify-center rounded-[50%]" +
  283. " bg-gradient-to-t from-[#ff611b] to-[#ffcf35]"
  284. }
  285. target={"_blank"}
  286. >
  287. <img
  288. className={"h-[0.3889rem] w-[0.3889rem] object-contain"}
  289. src={item.icon_url}
  290. alt={""}
  291. ></img>
  292. </Link>
  293. ))}
  294. </>
  295. );
  296. };
  297. const getMaxSignId = (obj: any) => {
  298. if (!obj) return null;
  299. const signArr: number[] = [];
  300. Object.keys(obj).map((key) => {
  301. if (obj[key] === 9) {
  302. signArr.push(Number(key));
  303. }
  304. });
  305. return signArr.length > 0 ? Math.max(...signArr) : null;
  306. };
  307. /**
  308. * 签到活动
  309. */
  310. const SignInSection: FC = () => {
  311. const { modalShow, closeModalShow } = useModalShow((state: any) => ({
  312. modalShow: state.modalShow,
  313. closeModalShow: state.closeModalShow,
  314. }));
  315. const SignInRef = useRef<SignInModalProps>(null);
  316. const [activityId, setActivityId] = useState<number | null>(null);
  317. const keyName = "SignInSection";
  318. const { getSignData, signData } = useSignStore((state) => {
  319. return {
  320. getSignData: state.getSignData,
  321. signData: state.signData,
  322. };
  323. });
  324. const getUserInfo = async () => {
  325. const res: any = await userInfoApi();
  326. if (res.code === 200 && res.data?.activity) {
  327. const activity_id = getMaxSignId(res.data?.activity);
  328. if (activity_id) {
  329. setActivityId(activity_id);
  330. await getSignData({ activity_id: activity_id });
  331. }
  332. }
  333. };
  334. const checkIsShowed = () => {
  335. const showTime = localStorage.getItem("sign");
  336. if (showTime) {
  337. const nextDay = dayjs(Number(showTime)).add(1, "day").format("YYYY-MM-DD");
  338. if (dayjs(nextDay).isAfter(dayjs())) {
  339. return true;
  340. }
  341. }
  342. return false;
  343. };
  344. useEffect(() => {
  345. // if (checkIsShowed()) return;
  346. if (getToken()) getUserInfo();
  347. // eslint-disable-next-line react-hooks/exhaustive-deps
  348. }, []);
  349. const destoryComponent = () => {
  350. closeModalShow(keyName);
  351. };
  352. useEffect(() => {
  353. console.log(modalShow, modalShow === keyName, getToken());
  354. if (modalShow === keyName && getToken()) {
  355. if (checkIsShowed()) {
  356. destoryComponent();
  357. } else {
  358. localStorage.setItem("sign", `${Date.now()}`);
  359. signInHandle();
  360. }
  361. }
  362. // eslint-disable-next-line react-hooks/exhaustive-deps
  363. }, [modalShow, keyName]);
  364. const signInHandle = () => {
  365. SignInRef.current?.onOpen();
  366. };
  367. if (!activityId) {
  368. return null;
  369. }
  370. return (
  371. <>
  372. {/* <div
  373. className={
  374. "mt-[0.2778rem] flex h-[0.54rem] w-[0.54rem] items-center justify-center" +
  375. " rounded-[50%]" +
  376. " bg-gradient-to-t from-[#ff611b] to-[#ffcf35]"
  377. }
  378. onClick={signInHandle}
  379. >
  380. Sign
  381. </div> */}
  382. <SignInModal ref={SignInRef} onDestory={destoryComponent}></SignInModal>
  383. </>
  384. );
  385. };
  386. const ServiceWidget: FC<Props> = (props) => {
  387. const { services } = props;
  388. const [type, setType] = useState<number>(0);
  389. const { eventView } = useEventPoint();
  390. const newServices = services?.filter((item) => item.status === 1) || [];
  391. const getWheel = () => {
  392. if (!getToken()) return Promise.resolve(undefined);
  393. return getWheelApi().then((res) => {
  394. return res.data;
  395. });
  396. };
  397. useEffect(() => {
  398. // 数据存储,侧边栏使用
  399. // setSocials(socials);
  400. // pixel 埋点
  401. // eventView();
  402. }, []);
  403. const t = useTranslations("HomePage");
  404. // const servicesMap = new Map<number, any>([
  405. // [
  406. // 0,
  407. // {
  408. // components: <SlotSection />,
  409. // },
  410. // ],
  411. // [
  412. // 1,
  413. // {
  414. // components: <WheelSection />,
  415. // },
  416. // ],
  417. // [
  418. // 2,
  419. // {
  420. // components: <PaySection />,
  421. // },
  422. // ],
  423. // [
  424. // 3,
  425. // {
  426. // components: <RedPacketSection />,
  427. // },
  428. // ],
  429. // [
  430. // 4,
  431. // {
  432. // components: <MessageSection />,
  433. // },
  434. // ],
  435. // [
  436. // 5,
  437. // {
  438. // components: <CustomerSection services={services} />,
  439. // },
  440. // ],
  441. // [
  442. // 6,
  443. // {
  444. // components: <SignInSection />,
  445. // },
  446. // ],
  447. // ]);
  448. // const curComponents = useMemo(() => {
  449. // return servicesMap.get(type)?.components;
  450. // // eslint-disable-next-line react-hooks/exhaustive-deps
  451. // }, [type, services, servicesMap]);
  452. // const modalDestory = () => {
  453. // setType(type + 1);
  454. // };
  455. // console.log("type:", type);
  456. return (
  457. <>
  458. <div
  459. className={`absolute bottom-[0.84rem] right-[0.12rem] z-50 flex w-[0.6944rem] flex-col items-center justify-center`}
  460. >
  461. {/* {React.cloneElement(curComponents, { onDestory: modalDestory })} */}
  462. <SlotSection />
  463. {/*轮盘 */}
  464. <WheelSection />
  465. {/*首充*/}
  466. <PaySection />
  467. {/* 红包雨icon */}
  468. <RedPacketSection />
  469. {/*未读消息*/}
  470. <MessageSection />
  471. {/*客服*/}
  472. <CustomerSection services={services} />
  473. {/* 签到 */}
  474. <SignInSection />
  475. </div>
  476. <div
  477. className={`grid`}
  478. style={{
  479. gridTemplateColumns: ` repeat(${newServices && newServices.length >= 5 ? 5 : (newServices?.length ?? 1)},1fr)`,
  480. }}
  481. >
  482. {newServices.map((service, index) => {
  483. return (
  484. <Link
  485. key={index}
  486. href={service.url}
  487. target={"_blank"}
  488. className="bg-white m-[0.05rem] h-[0.3889rem] w-[0.3889rem] rounded"
  489. >
  490. <img
  491. className={"h-[0.3889rem] w-[0.3889rem]"}
  492. src={service.icon_url}
  493. ></img>
  494. </Link>
  495. );
  496. })}
  497. </div>
  498. <div className={"text-[#ced1ff]"}>{t("Service")}</div>
  499. {/*share*/}
  500. <div className={"my-[0.2rem] text-[0.12rem] text-[#ced1ff]"}>{t("Share")}</div>
  501. <Image
  502. src={"/logo2.png"}
  503. alt={"BCWIN777"}
  504. width={120}
  505. height={180}
  506. className={"mb-[0.2rem]"}
  507. />
  508. </>
  509. );
  510. };
  511. export default ServiceWidget;